package com.cgutech.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

public class NettyClient {
    private DeviceConnectData device;

    private Bootstrap bootstrap;
    private ChannelFuture channelFuture;

    private boolean isRun;
    private boolean isConnected;
    private String uid;
    public interface ClientDataReceiver {
        void onData(String dev, byte[] data);
    }
    public ClientDataReceiver receiver;

    /**
     * 实例化Netty客户端构造函数
     * @param bootstrap Netty启动器
     */
    public NettyClient(DeviceConnectData device, Bootstrap bootstrap, String uid) {
        this.device = device;
        this.bootstrap = bootstrap;
        isRun = true;
        this.uid = uid;
    }

    public void connect() throws InterruptedException {
        if(isRun && !isConnected) {
            final GenericFutureListener<Future<? super Void>> listener = new GenericFutureListener<Future<? super Void>>() {
                public void operationComplete(Future<? super Void> future) throws Exception {
                    future.removeListener((GenericFutureListener)this); // 连接完成，删除监听器
                    if(!future.isSuccess()) {
                        System.out.println("[" + device.getIp() + "]连接失败");
                        future.cause().printStackTrace();
                        if(isRun) {
                            isConnected = false;
                            Thread.sleep(3000);
                            connect();
                        }
                    } else {
                        isConnected = true;
                        System.out.println("[" + device.getIp() + "]连接成功");
                        channelFuture.channel().pipeline().addLast(new MyChannelInboundHandlerAdapter(receiver)); // 连接成功之后添加数据监听、连接监听器
                    }
                }
            };
            channelFuture = bootstrap.connect(device.getIp(), device.getPort()).addListener(listener);
        }
    }

    public void write(byte[] data) {
        if(channelFuture != null && !channelFuture.isVoid()) {
            channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer(data));
        }
    }

    public void close() {
        isRun = false;
        if(channelFuture != null && !channelFuture.isVoid()) {
            channelFuture.channel().close();
            channelFuture = null;
        }
    }

    public boolean isConnected() {
        return isConnected;
    }

    /**
     * 数据接收器
     */
    private class MyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
        private ClientDataReceiver receiver;
        public MyChannelInboundHandlerAdapter(ClientDataReceiver receiver) {
            this.receiver = receiver;
        }
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            try {
                ByteBuf bb = (ByteBuf) msg;
                byte[] respByte = new byte[bb.readableBytes()];
                bb.getBytes(0, respByte);
                receiver.onData(uid, respByte);
//                System.out.println("收到数据：" + new String(respByte));
            } finally {
                ReferenceCountUtil.release(msg);
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            // super.channelInactive(ctx);
            ctx.close();
            System.out.println("连接断开");
            isConnected = false;
            if(isRun) {
                Thread.sleep(3000);
                connect();
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            System.out.println("异常");
            cause.printStackTrace();
            ctx.close();
            isConnected = false;
            if(isRun) {
                Thread.sleep(3000);
                connect();
            }
        }
    };
}
